package org.fujene.memoryblock.repli;

import static org.fujene.memoryblock.repli.ReplicateDictionaryBlock.Type.*;

import java.io.IOException;
import java.io.UnsupportedEncodingException;

import org.fujene.memoryblock.MemoryBlock;
import org.fujene.structure.Constants;
import org.fujene.structure.ExtendTerm;
import org.fujene.structure.LookupPointer;
import org.fujene.toolbox.ByteConverter;

public class ReplicateDictionaryBlock extends MemoryBlock{
	private int opt_length = 0;
	// 検索種別の数
	private int opt_type = 1;

	public static enum Type{
		ALL, NEXT, DOCID, INVINDEX, STR, INVDOC, DOCFRQ,
	}

	private class DictionaryTerm{
		private int from;
		private LookupPointer nextTerm;
		private int docfreq;
		private LookupPointer[] invIndex;
		private long[] firstID;
		private int strlen;
		private String str;
		private Type type;

		// データ読み込み用
		public DictionaryTerm(int from, Type type){
			this.from = from;
			this.type = type;
			// Next term (0 - 6B)
			if (type == ALL || type == NEXT)
				this.nextTerm = ByteConverter.getPtr(getBody(from, PTR));
			else this.nextTerm = Constants.EMPTY_PTR;
			from += PTR;

			// Document frequency (6 - 9B)
			if (type == ALL || type == DOCFRQ)
				this.docfreq = ByteConverter.byteToInt(getBody(from, DOCFREQ));
			else this.docfreq = -1;
			from += DOCFREQ;

			if (type == ALL || type == INVINDEX || type == DOCID || type == INVDOC){
				this.invIndex = new LookupPointer[opt_type];
				this.firstID = new long[opt_type];
				for(int i = 0; i < opt_type; i++){
					// Pointer to invert indices(+0 - +6B)
					this.invIndex[i] = ByteConverter.getPtr(getBody(from, PTR));
					from += PTR;
					// First IDs(+6 - +14B)
					this.firstID[i] = ByteConverter.byteToLong(getBody(from, 8));
					from += 8;
				}
			}

			if (type == ALL || type == STR){
				// Length of term(++0 - ++2B)
				this.strlen = ByteConverter.byteToInt(getBody(from, STRLEN));
				from += STRLEN;
				try{
					// Term
					this.str = new String(getBody(from, this.strlen), "UTF-8");
				}
				catch(UnsupportedEncodingException e){}
			}
		}

		// データ書き込み用
		public DictionaryTerm(int from, LookupPointer nextTerm, String str){
			this.from = from;
			this.type = ALL;
			if (nextTerm != null)
				this.nextTerm = nextTerm;
			else this.nextTerm = Constants.EMPTY_PTR;

			this.docfreq = 1;
			this.invIndex = new LookupPointer[opt_type];
			this.firstID = new long[opt_type];
			for(int i = 0; i < opt_type; i++){
				this.invIndex[i] = Constants.EMPTY_PTR;
				this.firstID[i] = -1;
			}
			this.str = str;
			try{
				this.strlen = str.getBytes("UTF-8").length;
			}catch(UnsupportedEncodingException e){}
			opt_length++;
		}

		public void write(){
			int tmp = this.from;

			if (type == ALL || type == NEXT) setBody(ByteConverter.setPtr(this.nextTerm), tmp);
			tmp += PTR;

			if (type == ALL || type == DOCFRQ) setBody(ByteConverter.intToByte(this.docfreq, DOCFREQ), tmp);
			tmp += DOCFREQ;

			for(int i = 0; i < opt_type; i++){
				if (type == ALL || type == INVINDEX || type == INVDOC)
					setBody(ByteConverter.setPtr(invIndex[i]), tmp);
				tmp += PTR;
				if (type == ALL || type == DOCID || type == INVDOC){
					setBody(ByteConverter.longToByte(firstID[i], 8), tmp);
				}
				tmp += 8;
			}
			if (type == ALL){
				setBody(ByteConverter.intToByte(strlen, STRLEN), tmp);
				tmp += STRLEN;
				try{
					byte[] b = str.getBytes("UTF-8");
					setBody(b, tmp);
				}
				catch(UnsupportedEncodingException e){}
			}
		}
	}

	public ReplicateDictionaryBlock(int bodySize, int ID, boolean isAllocate){
		super(bodySize, ID, Constants.MEMORY_TYPE_DICT, isAllocate);
	}

	public LookupPointer getNextPointer(int ptr){
		DictionaryTerm ptredDic = new DictionaryTerm(ptr, NEXT);

		if (ptredDic.nextTerm.equals(Constants.EMPTY_PTR))
			return null;
		else return ptredDic.nextTerm;
	}

	public LookupPointer getInvIndexPointer(int ptr, int index){
		DictionaryTerm ptredDic = new DictionaryTerm(ptr, INVDOC);

		if (ptredDic.invIndex[index].equals(Constants.EMPTY_PTR))
			return null;
		else{
			LookupPointer retPtr = ptredDic.invIndex[index];
			retPtr.firstID = ptredDic.firstID[index];
			return ptredDic.invIndex[index];
		}
	}

	public long getFirstID(int ptr, int index){
		DictionaryTerm ptredDic = new DictionaryTerm(ptr, DOCID);

		return ptredDic.firstID[index];
	}

	// 情報読み込み
	public ExtendTerm getTerm(int ptr, int indexType){
		DictionaryTerm ptredDic = new DictionaryTerm(ptr, ALL);

		if (ptredDic.nextTerm.equals(Constants.EMPTY_PTR)) ptredDic.nextTerm = null;
		if (ptredDic.invIndex[indexType].equals(Constants.EMPTY_PTR)) ptredDic.invIndex[indexType] = null;

		return new ExtendTerm(ptredDic.str, ptredDic.nextTerm, null, new LookupPointer(this.getID(), ptr),
				-1, ptredDic.invIndex, ptredDic.firstID, ptredDic.docfreq);
	}

	public void incrementDocfreq(int ptr){
		DictionaryTerm ptredDic = new DictionaryTerm(ptr, DOCFRQ);

		ptredDic.docfreq++;

		ptredDic.write();
	}

	public void setTerm(int ptr, LookupPointer next, String str){
		DictionaryTerm newTerm = new DictionaryTerm(ptr, next, str);

		newTerm.write();
	}

	public void setInvIndex(int ptr, LookupPointer docIDptr, long firstID, int indexType){
		DictionaryTerm setTerm = new DictionaryTerm(ptr, INVDOC);
		setTerm.invIndex[indexType] = docIDptr != null ? docIDptr : Constants.EMPTY_PTR;
		setTerm.firstID[indexType] = firstID;

		setTerm.write();
	}

	public void setNextPointer(int ptr, LookupPointer next){
		DictionaryTerm prevTerm = new DictionaryTerm(ptr, NEXT);
		prevTerm.nextTerm = next != null ? next : Constants.EMPTY_PTR;

		prevTerm.write();
	}

	// サイズ確認なし
//	public int getNewPointer(){
//		return this.bodySize - this.bodyVacancy;
//	}

	// サイズ確認つき
	public int getNewPointer(String str){
		try{
			int length = PTR + DOCFREQ + (opt_type * (PTR + 8)) + STRLEN + str.getBytes("UTF-8").length;
			if (this.bodyVacancy >= length){
				this.bodyVacancy -= length;
				return this.bodySize - this.bodyVacancy - length;
			}else{
				return -1;
			}
		}
		catch(UnsupportedEncodingException e){
			return -1;
		}
	}

	@Override
	public int getOptionSize(){
		return 2;
	}

	@Override
	public byte[] getOptionByteStream(){
		byte[] form = new byte[4 * 2];
		System.arraycopy(ByteConverter.intToByte(opt_length, 4), 0, form, 0, 4);
		System.arraycopy(ByteConverter.intToByte(opt_type, 4), 0, form, 4, 4);
		return form;
	}

	public int getTermCount(){
		return opt_length;
	}

	@Override
	public void setOption(int option, int value){
		if (option == 0)
			opt_length = value;
		else if (option == 1) opt_type = value;
	}

	@Override
	public String getClassName(){
		return new String("ExtentDictionaryBlock");
	}

	@Override
	public byte[] getData() throws IOException{
		throw new NoSuchMethodError("Unimplemented method");
	}

	@Override
	public void setData(byte[] array) throws IOException{
		throw new NoSuchMethodError("Unimplemented method");
	}
}
